
#include <colorpicker/colorpicker.h>
#include <qgridlayout.h>
#include <math.h>
#include <qgl.h>
#include <GL/gl.h>
#include <qevent.h>
#include <math/matrix.h>
#include <math/vector.h>
#include <config.h>
#include <qcolordialog.h>


class cceColorPickerWheel : public QGLWidget
{
private:
  enum State
  {
    State_Idle,
    State_Hue,
    State_SaturateValue
  };

  unsigned  _details;

  GLuint    _vbo_v;
  GLuint    _vbo_c;

  ceVector2f  _v0;
  ceVector2f  _v1;
  ceVector2f  _v2;

  QColor    _color;

  float     _distInner;
  float     _distOuter;

  State     _state;



public:
  cceColorPickerWheel (QWidget* parent = 0)
    : QGLWidget(parent)
    , _details (256)
    , _vbo_v(0)
    , _vbo_c(0)
    , _color (255, 128, 64)
    , _distOuter (0.99f)
    , _distInner(0.85f)
    , _state (State_Idle)
  {
    unsigned size = 256;
    setMinimumSize(size, size);
    setMaximumSize(size, size);
  }

  void SetColor (const QColor &color)
  {
    _color = color;
    repaint();
  }

  const QColor& GetColor () const
  {
    return _color;
  }

protected:
  void resizeGL(int w, int h)
  {
    glViewport (0, 0, w, h);
  }

  void paintGL()
  {
    QPalette pal;
    QColor bgColor = pal.color(QPalette::Background);

    float w = (float)width();
    float h = (float)height();

    float cx = w / 2.0f;
    float cy = h / 2.0f;

    float cxOut = cx * _distOuter;
    float cyOut = cy * _distOuter;
    float cxIn  = cx * _distInner;
    float cyIn  = cy * _distInner;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0f, (float)width(), 0.0, (float)height(), -1.0, 1.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glClearColor(bgColor.redF(), bgColor.greenF(), bgColor.blueF(), 1.0f);
    glClear (GL_COLOR_BUFFER_BIT);

    glDisable(GL_DEPTH_TEST);

    glBegin(GL_TRIANGLE_STRIP);
    for (unsigned i=0; i<_details; ++i)
      {
        float t = (float)i / (float)(_details-1);
        float a = t * M_PI * 2.0f;

        float ca = (float)cos (a);
        float sa = (float)sin (a);

        QColor col = QColor::fromHsvF(t, 1.0f, 1.0f);

        glColor3f (col.redF(), col.greenF(), col.blueF());


        glVertex2f(cx + ca * cxIn,  cy + sa * cxIn);
        glVertex2f(cx + ca * cxOut, cy + sa * cxOut);
      }
    glEnd ();


    float hue = abs(_color.hueF());
    printf ("hue: %f\n", hue);
    fflush(stdout);

    float a0 = hue * M_PI * 2.0f;
    float a1 = (hue + 0.333333f) * M_PI * 2.0f;
    float a2 = (hue + 0.666666f) * M_PI * 2.0f;

    glBegin (GL_TRIANGLES);

    QColor c0 = QColor::fromHsvF(hue, 1.0f, 1.0f);
    glColor3f (c0.redF(), c0.greenF(), c0.blueF());

    _v0 = ceVector2f (cx + (float)cos(a0) * cxIn, cy + (float)sin(a0) * cxIn);
    glVertex2f(_v0.x, _v0.y);


    QColor c1 = QColor::fromHsvF(hue, 1.0, 0.0f);
    glColor3f (c1.redF(), c1.greenF(), c1.blueF());
    _v1 = ceVector2f (cx + (float)cos(a1) * cxIn, cy + (float)sin(a1) * cxIn);
    glVertex2f(_v1.x, _v1.y);

    QColor c2 = QColor::fromHsvF(hue, 0.0, 1.0f);
    glColor3f (c2.redF(), c2.greenF(), c2.blueF());
    _v2 = ceVector2f (cx + (float)cos(a2) * cxIn, cy + (float)sin(a2) * cxIn);
    glVertex2f(_v2.x, _v2.y);

    glEnd ();


    glLineWidth(3.0f);
    glBegin(GL_LINES);
    glColor3f (0.5f, 0.5f, 0.5f);
    glVertex2f(cx + (float)cos(a0) * cxIn,  cy + (float)sin(a0) * cyIn);
    glVertex2f(cx + (float)cos(a0) * cxOut, cy + (float)sin(a0) * cyOut);
    glEnd ();



    ceVector3f v = pos (_color);
    ceVector2f cv = v.x*_v0 + v.y*_v1 + v.z*_v2;
    float dist = 6.0f;

    glLineWidth(2.0f);
    glBegin (GL_LINE_STRIP);
    glColor3f (0.5f, 0.5f, 0.5f);
    for (unsigned i=0; i<16; ++i)
      {
        float t = (float)i / 15.0f;
        float a = t * M_PI * 2.0f;
        glVertex2f (cv.x + cos (a) * dist, cv.y + sin (a) * dist);
      }
    glEnd ();

  }

  void initializeGL()
  {
  }

  void mousePressEvent(QMouseEvent *evt)
  {
    float cx = (float)width() / 2.0f;
    float cy = (float)height() / 2.0f;

    float dx = (float)evt->x() - cx;
    float dy = (float)evt->y() - cy;

    float d = sqrt (dx*dx + dy*dy);

    ceVector2f m (evt->x(), height () - evt->y());
    ceVector3f p = pos (m, _v0, _v1, _v2);

    if (d >= _distInner * cx &&  d <= _distOuter * cx && _state == State_Idle)
      {
        ceVector2f d (dx, dy);
        d.Normalize();
        UpdateHue (d);
        _state = State_Hue;
        grabMouse();
      }
    else if (p.x >= 0.0f && p.x <= 1.0f &&
             p.y >= 0.0f && p.y <= 1.0f &&
             p.z >= 0.0f && p.z <= 1.0f)
      {
        UpdateSaturateValue (m);
        _state = State_SaturateValue;
        grabMouse ();
      }
  }

  void mouseMoveEvent(QMouseEvent *evt)
  {
    if (_state == State_Hue)
      {
        float cx = (float)width() / 2.0f;
        float cy = (float)height() / 2.0f;

        float dx = (float)evt->x() - cx;
        float dy = (float)evt->y() - cy;

        ceVector2f d (dx, dy);
        d.Normalize();
        UpdateHue (d);
      }
    else if (_state == State_SaturateValue)
      {
        ceVector2f m (evt->x(), height () - evt->y());
        UpdateSaturateValue (m);
      }
  }

  void mouseReleaseEvent(QMouseEvent *evt)
  {
    if (_state != State_Idle)
      {
        releaseMouse();
        _state = State_Idle;
      }
  }


private:

  ceVector3f pos (const QColor& c)
  {
    ceVector2f v0 (1.0f, 1.0f);
    ceVector2f v1 (1.0f, 0.0f);
    ceVector2f v2 (0.0f, 1.0f);

    float cx = c.saturationF();
    float cy = c.valueF();

    return pos (ceVector2f (cx, cy), v0, v1, v2);
  }

  ceVector3f pos (const ceVector2f& p, const ceVector2f& v0, const ceVector2f& v1, const ceVector2f& v2)
  {
    float l0 = ((v1.y-v2.y) * (p.x-v2.x) + (v2.x-v1.x) * (p.y-v2.y)) / ((v1.y-v2.y) * (v0.x-v2.x) + (v2.x-v1.x) * (v0.y-v2.y));
    float l1 = ((v2.y-v0.y) * (p.x-v2.x) + (v0.x-v2.x) * (p.y-v2.y)) / ((v1.y-v2.y) * (v0.x-v2.x) + (v2.x-v1.x) * (v0.y-v2.y));

    return ceVector3f (l0, l1, 1.0f - l0 - l1);
  }

  void UpdateHue (const ceVector2f &dir)
  {
    float h = dir.Angle(ceVector2f (1.0f, 0.0f));

    if (dir.y > 0.0)
      {
        h = M_PI + (M_PI - h);
      }

    h /= (2.0f * M_PI);

    _color = QColor::fromHsvF(h, _color.saturationF(), _color.valueF());
    repaint();
  }

  void UpdateSaturateValue (const ceVector2f &m)
  {
    ceVector2f v0 (1.0f, 1.0f);
    ceVector2f v1 (1.0f, 0.0f);
    ceVector2f v2 (0.0f, 1.0f);

    ceVector3f p = pos(m, _v0, _v1, _v2);
    if (p.x < 0.0f) p.x = 0.0f;
    if (p.x > 1.0f) p.x = 1.0f;
    if (p.y < 0.0f) p.y = 0.0f;
    if (p.y > 1.0f) p.y = 1.0f;
    if (p.z < 0.0f) p.z = 0.0f;
    if (p.z > 1.0f) p.z = 1.0f;

    float l = p.x+p.y+p.z;

    p.x /= l;
    p.y /= l;
    p.z /= l;

    ceVector2f sv = p.x*v0 + p.y*v1 + p.z*v2;
    if (sv.x < 0.0f) sv.x = 0.0f;
    if (sv.x > 1.0f) sv.x = 1.0f;
    if (sv.y < 0.0f) sv.y = 0.0f;
    if (sv.y > 1.0f) sv.y = 1.0f;

    _color = QColor::fromHsvF(_color.hueF(), sv.x, sv.y);
    repaint();

  }

};

cceColorPicker::cceColorPicker(QWidget* parent)
  : QWidget (parent)
{
  QGridLayout* layout = new QGridLayout(this);

  _wheel = new cceColorPickerWheel (this);
  layout->addWidget(_wheel);
}


void cceColorPicker::SetColor(const QColor &color)
{
  _wheel->SetColor(color);
}

const QColor& cceColorPicker::GetColor () const
{
  return _wheel->GetColor();
}


cceColorPickerDialog::cceColorPickerDialog (QWidget *parent)
  : QDialog (parent)
  , _color (255, 255, 255)
{
  _gui.setupUi(this);

  QGridLayout* layout = new QGridLayout(_gui.frameContent);

  _picker = new cceColorPicker(this);
  _picker->SetColor(_color);
  layout->addWidget(_picker);
}

void cceColorPickerDialog::on_pushButtonOk_clicked()
{
  _color = _picker->GetColor();
  accept();
}

void cceColorPickerDialog::on_pushButtonCancel_clicked()
{
  reject();
}


void cceColorPickerDialog::SetColor(const QColor &color)
{
  _color = color;
  _picker->SetColor(color);
}

const QColor& cceColorPickerDialog::GetColor () const
{
  return _color;
}


QColor cceColorPickerDialog::GetColor(QWidget *parent, const QColor &color, const QString &windowTitle)
{
  QString title = "Select color - " CCE_TOOL_NAME;
  if (windowTitle != QString::Null ())
    {
      title = windowTitle;
    }


#if 0
  cceColorPickerDialog dlg (parent);
  dlg.SetColor(color);


  dlg.exec();
  return dlg.GetColor();
#else
  QColor result = QColorDialog::getColor(color, parent, title);
  if (result.isValid())
    {
      return result;
    }
  return color;
#endif
}
